#include "servicemanager.h"

#include "service.h"
#include "servicekey.h"

namespace infinity {

ServiceManager::ServiceManager()
:	m_servicesAreSorted(true)
,	m_updatesMedia(true)
{
}

ServiceManager::~ServiceManager()
{
	//! @todo dwAssert(m_services.empty(), "Some services have not been unregistered and won't be destroyed");
}

void ServiceManager::registerDependency(const ServiceKey& service, const ServiceKey& dependency)
{
	dwAssert(&findServiceHolder(service) != &InvalidHolder, "Invalid service, service is not registered");
	dwAssert(&findServiceHolder(dependency) != &InvalidHolder, "Invalid service, service is not registered");
	dwAssert(!serviceDependsOn(dependency, service), "Circular dependency detected");

	if (!serviceDependsOn(dependency, service))
	{
		ServiceHolder& serviceHolder = findServiceHolder(service);

		serviceHolder.dependencies.push_back(dependency);
	}

	m_servicesAreSorted = false;
}

void ServiceManager::update(dw::float32 deltaT)
{
	if (!m_servicesAreSorted)
	{
		sortServices();
	}

	// Initialize Simulation && Media
	for (ServicesColletion::iterator serviceIt = m_services.begin(); serviceIt != m_services.end(); ++serviceIt)
	{
		ServiceHolder& svcHolder = *serviceIt;
		Service* service = svcHolder.service;

		dwAssert(service != NULL, "Invalid service");

		if (service->needsSimulationInitialization())
		{
			service->initializeSimulation();
		}
		if (service->isReadyForSimulation() && dependenciesAreReadyForSimulation(svcHolder))
		{
			service->updateSimulation(deltaT);
		}
		if (m_updatesMedia && dependenciesAreReadyForMedia(svcHolder))
		{
			if (service->needsMediaInitialization())
			{
				service->initializeMedia();
			}
			if (service->isReadyForMedia() && dependenciesAreReadyForMedia(svcHolder))
			{
				service->updateMedia(deltaT);
			}
		}
	}
}

void ServiceManager::destroyAllServices()
{
	//! @todo terminate all services
}

/*virtual*/ void ServiceManager::renderDebug()
{
	// Initialize Simulation && Media
	for (ServicesColletion::iterator serviceIt = m_services.begin(); serviceIt != m_services.end(); ++serviceIt)
	{
		ServiceHolder& svcHolder = *serviceIt;
		Service* service = svcHolder.service;

		dwAssert(service != NULL, "Invalid service");

		service->renderDebug();
	}
}

ServiceManager::ServiceHolder& ServiceManager::findServiceHolder(const ServiceKey& key)
{
	for (ServicesColletion::iterator serviceIt = m_services.begin(); serviceIt != m_services.end(); ++serviceIt)
	{
		if (serviceIt->key == key)
		{
			return *serviceIt;
		}
	}

	return const_cast< ServiceHolder& >(InvalidHolder);
}

const ServiceManager::ServiceHolder& ServiceManager::findServiceHolder(const ServiceKey& key) const
{
	for (ServicesColletion::const_iterator serviceIt = m_services.begin(); serviceIt != m_services.end(); ++serviceIt)
	{
		if (serviceIt->key == key)
		{
			return *serviceIt;
		}
	}

	return InvalidHolder;
}

bool ServiceManager::serviceDependsOn(const ServiceKey& service, const ServiceKey& dependency) const
{
	const ServiceHolder& svcHolder = findServiceHolder(service);

	for (DependenciesCollection::const_iterator depIt = svcHolder.dependencies.begin(); depIt != svcHolder.dependencies.end(); ++depIt)
	{
		if (*depIt == dependency || serviceDependsOn(*depIt, dependency))
		{
			return true;
		}
	}

	return false;
}

struct ServiceDependencyComp
{
	bool operator () (const ServiceManager::ServiceHolder& i, const ServiceManager::ServiceHolder& j)
	{
		return manager->serviceDependsOn(j.key, i.key);
	}

	ServiceManager* manager;
};

void ServiceManager::sortServices()
{
	ServiceDependencyComp comp;

	comp.manager = this;
	std::sort(m_services.begin(), m_services.end(), comp);

	m_servicesAreSorted = true;
}

bool ServiceManager::dependenciesAreReadyForSimulation(const ServiceHolder& svcHolder) const
{
	bool dependenciesAreReady = true;

	for (DependenciesCollection::const_iterator depIt = svcHolder.dependencies.begin();
		depIt != svcHolder.dependencies.end() && dependenciesAreReady; ++depIt)
	{
		const ServiceHolder& dependencyHolder = findServiceHolder(*depIt);

		dependenciesAreReady = dependenciesAreReady &&
			dependencyHolder.service->isReadyForSimulation() &&
			dependenciesAreReadyForSimulation(dependencyHolder);
	}

	return dependenciesAreReady;
}

bool ServiceManager::dependenciesAreReadyForMedia(const ServiceHolder& svcHolder) const
{
	bool dependenciesAreReady = true;

	for (DependenciesCollection::const_iterator depIt = svcHolder.dependencies.begin();
		depIt != svcHolder.dependencies.end() && dependenciesAreReady; ++depIt)
	{
		const ServiceHolder& dependencyHolder = findServiceHolder(*depIt);

		dependenciesAreReady = dependenciesAreReady &&
			dependencyHolder.service->isReadyForMedia() &&
			dependenciesAreReadyForMedia(dependencyHolder);
	}

	return dependenciesAreReady;
}

/*static*/ const ServiceManager::ServiceHolder ServiceManager::InvalidHolder;

} // namespace infinity
